/***************************************************
*		 Copyright (c) 2018 MINE 田宇
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*
***************************************************/

#include "AHCI.h"
#include "SATA.h"
#include "block.h"
#include "lib.h"
#include "printk.h"
#include "interrupt.h"
#include "PCI.h"
#include "APIC.h"
#include "memory.h"


struct request_queue AHCI_request;

struct PCI_Header_00 AHCI_PCI_HBA;
struct HBA_Memory_Registers HBA;

struct Command_list *cmdheader = NULL;
struct Command_Table *cmdtbl = NULL;
struct PRDT *prdt = NULL;

struct H2DFIS *h2dfis = NULL;

struct Received_FIS *RecFIS = NULL;

long AHCI_cmd_out()
{
	wait_queue_T *wait_queue_tmp = container_of(list_next(&AHCI_request.wait_queue_list.wait_list),wait_queue_T,wait_list);
	struct block_buffer_node * node = AHCI_request.in_using = container_of(wait_queue_tmp,struct block_buffer_node,wait_queue);
	list_del(&AHCI_request.in_using->wait_queue.wait_list);
	AHCI_request.block_request_count--;

	switch(node->cmd)
	{
		case ATA_CMD_WRITE_DMA:

			///config Command List Structure(include a lot of Command Header,the Command Header point to a Command Table)
			memset(cmdheader,0,sizeof(struct Command_Table_Header));	///set Physical Region Descriptor Byte Count (PRDBC) == 0
			cmdheader->CTBL[0].CFL = (sizeof(struct H2DFIS) / sizeof(int));	////Command FIS Length (CFL)
			cmdheader->CTBL[0].A = 0;	////1:ATAPI (A)
			cmdheader->CTBL[0].W = 1;	////0:read,1:write
			cmdheader->CTBL[0].C = 1;	////1:clear busy upon R_OK
			cmdheader->CTBL[0].P = 1;	////1:Prefetchable
			cmdheader->CTBL[0].PRDTL = 1;	////Physical Region Descriptor Table Length (PRDTL)
			cmdheader->CTBL[0].CTBA = (unsigned int)Virt_To_Phy(cmdtbl) & 0xffffffff;
			cmdheader->CTBL[0].CTBAU = (unsigned int) (((unsigned long)Virt_To_Phy(cmdtbl) >> 32) & 0xffffffff);

			///Command Table(include CFIS/ACMD/PRDT Structures)
			///config CFIS(int Command Table)
			memset(cmdtbl,0,sizeof(struct Command_Table) + sizeof(struct PRDT) * cmdheader->CTBL[0].PRDTL);	
			h2dfis = (struct H2DFIS *)cmdtbl->CMD_FIS;
			h2dfis->FIS_Type = AHCI_FIS_TYPE_HOST2DEVICE_FIS;			////h2d
			h2dfis->C = 1;			///1:Command register,0:Device Control register
			h2dfis->Command = ATA_CMD_WRITE_DMA;	///ATA CMD WRITE DMA
			h2dfis->LBA0 = node->LBA & 0xff;
			h2dfis->LBA1 = (node->LBA >> 8)  & 0xff;
			h2dfis->LBA2 = (node->LBA >> 16) & 0xff;
			h2dfis->LBA3 = (node->LBA >> 24) & 0xff;
			h2dfis->LBA4 = (node->LBA >> 32) & 0xff;
			h2dfis->LBA5 = (node->LBA >> 40) & 0xff;
			h2dfis->Count = node->count;
			h2dfis->Device = 0xe0;

			///config PRDT (int Command Table)
			prdt->DBA = (unsigned int)Virt_To_Phy(node->buffer) & 0xffffffff;
			prdt->DBAU = (unsigned int) (((unsigned long)Virt_To_Phy(node->buffer) >> 32) & 0xffffffff);
			prdt->DBC = node->count * 512 - 1;	////Data Byte Count (DBC)
			prdt->I = 1;	////Interrupt on Completion (I)

			break;

		case ATA_CMD_READ_DMA:

			///config Command List Structure(include a lot of Command Header,the Command Header point to a Command Table)
			memset(cmdheader,0,sizeof(struct Command_Table_Header));	///set Physical Region Descriptor Byte Count (PRDBC) == 0
			cmdheader->CTBL[0].CFL = (sizeof(struct H2DFIS) / sizeof(int));	////Command FIS Length (CFL)
			cmdheader->CTBL[0].A = 0;	////1:ATAPI (A)
			cmdheader->CTBL[0].W = 0;	////0:read,1:write
			cmdheader->CTBL[0].C = 1;	////1:clear busy upon R_OK
			cmdheader->CTBL[0].P = 1;	////1:Prefetchable
			cmdheader->CTBL[0].PRDTL = 1;	////Physical Region Descriptor Table Length (PRDTL)
			cmdheader->CTBL[0].CTBA = (unsigned int)Virt_To_Phy(cmdtbl) & 0xffffffff;
			cmdheader->CTBL[0].CTBAU = (unsigned int) (((unsigned long)Virt_To_Phy(cmdtbl) >> 32) & 0xffffffff);

			///Command Table(include CFIS/ACMD/PRDT Structures)
			///config CFIS(int Command Table)
			memset(cmdtbl,0,sizeof(struct Command_Table) + sizeof(struct PRDT) * cmdheader->CTBL[0].PRDTL);	
			h2dfis = (struct H2DFIS *)cmdtbl->CMD_FIS;
			h2dfis->FIS_Type = AHCI_FIS_TYPE_HOST2DEVICE_FIS;			////h2d
			h2dfis->C = 1;			///1:Command register,0:Device Control register
			h2dfis->Command = ATA_CMD_READ_DMA;	///ATA CMD READ DMA
			h2dfis->LBA0 = node->LBA & 0xff;
			h2dfis->LBA1 = (node->LBA >> 8)  & 0xff;
			h2dfis->LBA2 = (node->LBA >> 16) & 0xff;
			h2dfis->LBA3 = (node->LBA >> 24) & 0xff;
			h2dfis->LBA4 = (node->LBA >> 32) & 0xff;
			h2dfis->LBA5 = (node->LBA >> 40) & 0xff;
			h2dfis->Count = node->count;
			h2dfis->Device = 0xe0;

			///config PRDT (int Command Table)
			prdt->DBA = (unsigned int)Virt_To_Phy(node->buffer) & 0xffffffff;
			prdt->DBAU = (unsigned int) (((unsigned long)Virt_To_Phy(node->buffer) >> 32) & 0xffffffff);
			prdt->DBC = node->count * 512 - 1;	////Data Byte Count (DBC)
			prdt->I = 1;	////Interrupt on Completion (I)

			break;

		case ATA_CMD_IDENTIFY_DISK:

			break;

		default:
			color_printk(BLACK,WHITE,"ATA CMD Error\n");
			break;
	}

//////////////////////////////////////////////////////////////////////////
	///bit 28-31:Interface Communication Control (ICC)
	///bit 04:FIS Receive Enable (FRE)
	///bit 01:Spin-Up Device (SUD)
	///bit 00:Start (ST)
	HBA.PxCR[0].PxCMD = HBA.PxCR[0].PxCMD | 0x10000003;

	///Port x Serial ATA Active
	HBA.PxCR[0].PxSACT = 1;

//	color_printk(PURPLE,BLACK,"1.port:%#010x(PxCLB),%#010x(PxCLBU),%#010x(PxFB),%#010x(PxFBU),%#010x(PxIS),%#010x(PxIE),%#010x(PxCMD),%#010x(PxTFD),%#010x(PxSIG),%#010x(PxSSTS),%#010x(PxSCTL),%#010x(PxSERR),%#010x(PxSACT),%#010x(PxCI),%#010x(PxSNTF),%#010x(PxFBS),%#010x(PxDEVSLP),%#018lx,%#018lx(PxVS)\n",HBA.PxCR[0].PxCLB,HBA.PxCR[0].PxCLBU,HBA.PxCR[0].PxFB,HBA.PxCR[0].PxFBU,HBA.PxCR[0].PxIS,HBA.PxCR[0].PxIE,HBA.PxCR[0].PxCMD,HBA.PxCR[0].PxTFD,HBA.PxCR[0].PxSIG, HBA.PxCR[0].PxSSTS,HBA.PxCR[0].PxSCTL,HBA.PxCR[0].PxSERR,HBA.PxCR[0].PxSACT,HBA.PxCR[0].PxCI,HBA.PxCR[0].PxSNTF,HBA.PxCR[0].PxFBS,HBA.PxCR[0].PxDEVSLP,HBA.PxCR[0].PxVS[1], HBA.PxCR[0].PxVS[0]);

	///Port x Command Issue
	HBA.PxCR[0].PxCI = 1;

	return 1;
}

void AHCI_end_request(struct block_buffer_node * node)
{
	if(node == NULL)
		color_printk(RED,BLACK,"end_request error\n");
//	color_printk(WHITE,BLACK,"end_request call\n");

	node->wait_queue.tsk->state = TASK_RUNNING;
	insert_task_queue(node->wait_queue.tsk);
//	node->wait_queue.tsk->flags |= NEED_SCHEDULE;
	current->flags |= NEED_SCHEDULE;

	kfree((unsigned long *)AHCI_request.in_using);
	AHCI_request.in_using = NULL;

	if(AHCI_request.block_request_count)
		AHCI_cmd_out();
}

void AHCI_add_request(struct block_buffer_node * node)
{
	list_add_to_before(&AHCI_request.wait_queue_list.wait_list,&node->wait_queue.wait_list);
	AHCI_request.block_request_count++;
}

void AHCI_read_handler(unsigned long nr, unsigned long parameter)
{
	struct block_buffer_node * node = ((struct request_queue *)parameter)->in_using;
	unsigned int value = 0;
	color_printk(RED,BLACK,"AHCI read_handler\n");

	node->count--;

//	color_printk(GREEN,BLACK,"2.port:%#010x(PxCLB),%#010x(PxCLBU),%#010x(PxFB),%#010x(PxFBU),%#010x(PxIS),%#010x(PxIE),%#010x(PxCMD),%#010x(PxTFD),%#010x(PxSIG),%#010x(PxSSTS),%#010x(PxSCTL),%#010x(PxSERR),%#010x(PxSACT),%#010x(PxCI),%#010x(PxSNTF),%#010x(PxFBS),%#010x(PxDEVSLP),%#018lx,%#018lx(PxVS)\n",HBA.PxCR[0].PxCLB,HBA.PxCR[0].PxCLBU,HBA.PxCR[0].PxFB,HBA.PxCR[0].PxFBU,HBA.PxCR[0].PxIS,HBA.PxCR[0].PxIE,HBA.PxCR[0].PxCMD,HBA.PxCR[0].PxTFD,HBA.PxCR[0].PxSIG, HBA.PxCR[0].PxSSTS,HBA.PxCR[0].PxSCTL,HBA.PxCR[0].PxSERR,HBA.PxCR[0].PxSACT,HBA.PxCR[0].PxCI,HBA.PxCR[0].PxSNTF,HBA.PxCR[0].PxFBS,HBA.PxCR[0].PxDEVSLP,HBA.PxCR[0].PxVS[1], HBA.PxCR[0].PxVS[0]);

	///Port x Interrupt Status
	value = HBA.PxCR[0].PxIS;
	if(value)
		HBA.PxCR[0].PxIS = value;

	///clear Interrupt Pending Status (IPS) [0]
	HBA.GHC->IS = 1;

	///Port x Serial ATA Error
	value = HBA.PxCR[0].PxSERR;
	if(value)
		HBA.PxCR[0].PxSERR = value;

	///bit 28-31:Interface Communication Control (ICC)
	///bit 04:FIS Receive Enable (FRE)
	///bit 01:Spin-Up Device (SUD)
	///bit 00:Start (ST)
	HBA.PxCR[0].PxCMD = HBA.PxCR[0].PxCMD & 0xfffffffe;

//	color_printk(YELLOW,BLACK,"3.port:%#010x(PxCLB),%#010x(PxCLBU),%#010x(PxFB),%#010x(PxFBU),%#010x(PxIS),%#010x(PxIE),%#010x(PxCMD),%#010x(PxTFD),%#010x(PxSIG),%#010x(PxSSTS),%#010x(PxSCTL),%#010x(PxSERR),%#010x(PxSACT),%#010x(PxCI),%#010x(PxSNTF),%#010x(PxFBS),%#010x(PxDEVSLP),%#018lx,%#018lx(PxVS)\n",HBA.PxCR[0].PxCLB,HBA.PxCR[0].PxCLBU,HBA.PxCR[0].PxFB,HBA.PxCR[0].PxFBU,HBA.PxCR[0].PxIS,HBA.PxCR[0].PxIE,HBA.PxCR[0].PxCMD,HBA.PxCR[0].PxTFD,HBA.PxCR[0].PxSIG, HBA.PxCR[0].PxSSTS,HBA.PxCR[0].PxSCTL,HBA.PxCR[0].PxSERR,HBA.PxCR[0].PxSACT,HBA.PxCR[0].PxCI,HBA.PxCR[0].PxSNTF,HBA.PxCR[0].PxFBS,HBA.PxCR[0].PxDEVSLP,HBA.PxCR[0].PxVS[1], HBA.PxCR[0].PxVS[0]);

	AHCI_end_request(node);
}

void AHCI_write_handler(unsigned long nr, unsigned long parameter)
{
	struct block_buffer_node * node = ((struct request_queue *)parameter)->in_using;
	unsigned int value = 0;
	color_printk(RED,BLACK,"AHCI write_handler\n");

	node->count--;

//	color_printk(WHITE,BLACK,"4.port:%#010x(PxCLB),%#010x(PxCLBU),%#010x(PxFB),%#010x(PxFBU),%#010x(PxIS),%#010x(PxIE),%#010x(PxCMD),%#010x(PxTFD),%#010x(PxSIG),%#010x(PxSSTS),%#010x(PxSCTL),%#010x(PxSERR),%#010x(PxSACT),%#010x(PxCI),%#010x(PxSNTF),%#010x(PxFBS),%#010x(PxDEVSLP),%#018lx,%#018lx(PxVS)\n",HBA.PxCR[0].PxCLB,HBA.PxCR[0].PxCLBU,HBA.PxCR[0].PxFB,HBA.PxCR[0].PxFBU,HBA.PxCR[0].PxIS,HBA.PxCR[0].PxIE,HBA.PxCR[0].PxCMD,HBA.PxCR[0].PxTFD,HBA.PxCR[0].PxSIG, HBA.PxCR[0].PxSSTS,HBA.PxCR[0].PxSCTL,HBA.PxCR[0].PxSERR,HBA.PxCR[0].PxSACT,HBA.PxCR[0].PxCI,HBA.PxCR[0].PxSNTF,HBA.PxCR[0].PxFBS,HBA.PxCR[0].PxDEVSLP,HBA.PxCR[0].PxVS[1], HBA.PxCR[0].PxVS[0]);

	///Port x Interrupt Status
	value = HBA.PxCR[0].PxIS;
	if(value)
		HBA.PxCR[0].PxIS = value;

	///clear Interrupt Pending Status (IPS) [0]
	HBA.GHC->IS = 1;

	///Port x Serial ATA Error
	value = HBA.PxCR[0].PxSERR;
	if(value)
		HBA.PxCR[0].PxSERR = value;

	///bit 28-31:Interface Communication Control (ICC)
	///bit 04:FIS Receive Enable (FRE)
	///bit 01:Spin-Up Device (SUD)
	///bit 00:Start (ST)
	HBA.PxCR[0].PxCMD = HBA.PxCR[0].PxCMD & 0xfffffffe;

//	color_printk(RED,BLACK,"5.port:%#010x(PxCLB),%#010x(PxCLBU),%#010x(PxFB),%#010x(PxFBU),%#010x(PxIS),%#010x(PxIE),%#010x(PxCMD),%#010x(PxTFD),%#010x(PxSIG),%#010x(PxSSTS),%#010x(PxSCTL),%#010x(PxSERR),%#010x(PxSACT),%#010x(PxCI),%#010x(PxSNTF),%#010x(PxFBS),%#010x(PxDEVSLP),%#018lx,%#018lx(PxVS)\n",HBA.PxCR[0].PxCLB,HBA.PxCR[0].PxCLBU,HBA.PxCR[0].PxFB,HBA.PxCR[0].PxFBU,HBA.PxCR[0].PxIS,HBA.PxCR[0].PxIE,HBA.PxCR[0].PxCMD,HBA.PxCR[0].PxTFD,HBA.PxCR[0].PxSIG, HBA.PxCR[0].PxSSTS,HBA.PxCR[0].PxSCTL,HBA.PxCR[0].PxSERR,HBA.PxCR[0].PxSACT,HBA.PxCR[0].PxCI,HBA.PxCR[0].PxSNTF,HBA.PxCR[0].PxFBS,HBA.PxCR[0].PxDEVSLP,HBA.PxCR[0].PxVS[1], HBA.PxCR[0].PxVS[0]);

	AHCI_end_request(node);
}

void AHCI_other_handler(unsigned long nr, unsigned long parameter)
{
	struct block_buffer_node * node = ((struct request_queue *)parameter)->in_using;
	color_printk(RED,BLACK,"AHCI other_handler\n");
	node->count--;
	AHCI_end_request(node);
}

struct block_buffer_node * AHCI_make_request(long cmd,unsigned long blocks,long count,unsigned char * buffer)
{
	struct block_buffer_node * node = (struct block_buffer_node *)kmalloc(sizeof(struct block_buffer_node),0);
	wait_queue_init(&node->wait_queue,current);

	switch(cmd)
	{
		case ATA_CMD_READ_DMA:
			node->end_handler = AHCI_read_handler;
			node->cmd = ATA_CMD_READ_DMA;
			break;

		case ATA_CMD_WRITE_DMA:
			node->end_handler = AHCI_write_handler;
			node->cmd = ATA_CMD_WRITE_DMA;
			break;

		default:
			node->end_handler = AHCI_other_handler;
			node->cmd = cmd;
			break;
	}

	node->LBA = blocks;
	node->count = count;
	node->buffer = buffer;

	return node;
}

void AHCI_submit(struct block_buffer_node * node)
{
	AHCI_add_request(node);

	if(AHCI_request.in_using == NULL)
		AHCI_cmd_out();
}

void AHCI_wait_for_finish()
{
	current->state = TASK_UNINTERRUPTIBLE;
	schedule();
}

long AHCI_open()
{
	color_printk(BLACK,WHITE,"AHCI Opened\n");
	return 1;
}

long AHCI_close()
{
	color_printk(BLACK,WHITE,"AHCI Closed\n");
	return 1;
}

long AHCI_ioctl(long cmd,long arg)
{
	struct block_buffer_node * node = NULL;

	if(cmd == ATA_CMD_IDENTIFY_DISK)
	{
		node = AHCI_make_request(cmd,0,0,(unsigned char *)arg);
		AHCI_submit(node);
		AHCI_wait_for_finish();
		return 1;
	}

	return 0;
}

long AHCI_transfer(long cmd,unsigned long blocks,long count,unsigned char * buffer)
{
	struct block_buffer_node * node = NULL;
	if(cmd == ATA_CMD_READ_DMA || cmd == ATA_CMD_WRITE_DMA)
	{
		node = AHCI_make_request(cmd,blocks,count,buffer);
		AHCI_submit(node);
		AHCI_wait_for_finish();
	}
	else
	{
		return 0;
	}

	return 1;
}

struct block_device_operation AHCI_device_operation =
{
	.open = AHCI_open,
	.close = AHCI_close,
	.ioctl = AHCI_ioctl,
	.transfer = AHCI_transfer,
};


unsigned long AHCI_install(unsigned long irq,void * arg)
{
	return 0;
}

void AHCI_enable(unsigned long irq)
{
}

hw_int_controller AHCI_int_controller =
{
	.enable = AHCI_enable,
	.disable = NULL,
	.install = AHCI_install,
	.uninstall = NULL,
	.ack = Local_APIC_edge_level_ack,
};

void AHCI_handler(unsigned long nr, unsigned long parameter, struct pt_regs * regs)
{
	struct block_buffer_node * node = ((struct request_queue *)parameter)->in_using;
	color_printk(BLACK,WHITE,"AHCI_handler,preempt:%d,pid:%d,cRSP:%#018lx,RSP:%#018lx,RIP:%#018lx\n",current->preempt_count,current->pid,get_rsp(),regs->rsp,regs->rip);
	node->end_handler(nr,parameter);
}

void AHCI_init()
{
	int bus,device,function;
	unsigned int index = 0;
	unsigned int value = 0;
	unsigned long * tmp = NULL;

	for(bus = 0;bus <= 255;bus++)
		for(device = 0;device <= 31;device++)
			for(function = 0;function <= 7;function++)
			{
				value = Read_PCI_Config(bus,device,function,0);
				if((value & 0xffff) == 0xffff)	//AHCI_PCI_HBA.VendorID
					continue;
				value = Read_PCI_Config(bus,device,function,8);
				if(((value >> 24) & 0xff) != 1)	//AHCI_PCI_HBA.ClassCode
					continue;
				if(((value >> 16) & 0xff) != 6)	//AHCI_PCI_HBA.SubClass
					continue;

				if(((value >> 16) & 0xff) == 6)	//AHCI_PCI_HBA.SubClass
					color_printk(YELLOW,BLACK,"-------------------------------------------------------\n");

				analysis_PCI_Config(&AHCI_PCI_HBA,bus,device,function);

				if(AHCI_PCI_HBA.SubClass != 6)
					continue;

				tmp = Phy_To_Virt(Get_gdt() + (((unsigned long)Phy_To_Virt(AHCI_PCI_HBA.Base32Address5) >> PAGE_GDT_SHIFT) & 0x1ff));
				if (*tmp == 0)
				{
					unsigned long * virtual = kmalloc(PAGE_4K_SIZE,0);
					memset(virtual,0,PAGE_4K_SIZE);
					set_mpl4t(tmp,mk_mpl4t(Virt_To_Phy(virtual),PAGE_KERNEL_GDT));
				}

				tmp = Phy_To_Virt((unsigned long *)(*tmp & (~ 0xfffUL)) + (((unsigned long)Phy_To_Virt(AHCI_PCI_HBA.Base32Address5) >> PAGE_1G_SHIFT) & 0x1ff));
				if(*tmp == 0)
				{
					unsigned long * virtual = kmalloc(PAGE_4K_SIZE,0);
					memset(virtual,0,PAGE_4K_SIZE);
					set_pdpt(tmp,mk_pdpt(Virt_To_Phy(virtual),PAGE_KERNEL_Dir));
				}

				tmp = Phy_To_Virt((unsigned long *)(*tmp & (~ 0xfffUL)) + (((unsigned long)Phy_To_Virt(AHCI_PCI_HBA.Base32Address5) >> PAGE_2M_SHIFT) & 0x1ff));
				set_pdt(tmp,mk_pdt(AHCI_PCI_HBA.Base32Address5 & PAGE_2M_MASK,PAGE_KERNEL_Page | PAGE_PWT | PAGE_PCD));

				flush_tlb();

				color_printk(YELLOW,BLACK,"-------------------------------------------------------\n");
				goto analysis_AHCI;
			}
analysis_AHCI:

	///init wait queue
	wait_queue_init(&AHCI_request.wait_queue_list,NULL);
	AHCI_request.in_using = NULL;
	AHCI_request.block_request_count = 0;

	///get GHC address and first Port register address
	HBA.PCI_Dev = &AHCI_PCI_HBA;
	HBA.GHC = (struct Generic_Host_Control *)Phy_To_Virt((unsigned long)AHCI_PCI_HBA.Base32Address5);
	HBA.PxCR = (struct Port_X_Control_Registers *)Phy_To_Virt((unsigned long)AHCI_PCI_HBA.Base32Address5 + 0x100);

	///bit 31:AHCI Enable (AE)
	///bit 01:Interrupt Enable (IE)
	///bit 00:HBA Reset (HR)
	HBA.GHC->GHC = 0x80000002;

	io_mfence();

	color_printk(BLUE,BLACK,"Generic Host Control(00h ~ ffh)\n");
	color_printk(BLUE,BLACK,"%#010x(CAP),%#010x(GHC),%#010x(IS),%#010x(PI)\n",HBA.GHC->CAP,HBA.GHC->GHC,HBA.GHC->IS,HBA.GHC->PI);
	color_printk(BLUE,BLACK,"%#010x(VS),%#010x(CCC_CTL),%#010x(CCC_PORTS),%#010x(EM_LOC)\n",HBA.GHC->VS,HBA.GHC->CCC_CTL,HBA.GHC->CCC_PORTs,HBA.GHC->EM_LOC);
	color_printk(BLUE,BLACK,"%#010x(struct HBA_Memory_RegistersEM_CTL),%#010x(CAP2),%#010x(BOHC)\n",HBA.GHC->EM_CTL,HBA.GHC->CAP2,HBA.GHC->BOHC);

	///bit 00-04:Number of Ports (NP)
	///color_printk(WHITE,BLACK,"HBA.GHC.CAP(NP) & 0x1f:%#02d\n",HBA.GHC->CAP & 0x1f);

	///>>>>>>>>get Received FIS Structure address
	RecFIS = (struct Received_FIS *)Phy_To_Virt(((unsigned long)HBA.PxCR[0].PxFBU << 32) | HBA.PxCR[0].PxFB);

	///>>>>>>>>get Command List Structure address(include a lot of Command Header,the Command Header point to a Command Table)
	cmdheader = (struct Command_list *)Phy_To_Virt(((unsigned long)HBA.PxCR[0].PxCLBU << 32) | HBA.PxCR[0].PxCLB);

	///>>>>>>>>alloc Command Table(include CFIS/ACMD/PRDT Structures)
	cmdtbl = (struct Command_Table *)kmalloc(sizeof(struct Command_Table) + sizeof(struct PRDT) * cmdheader->CTBL[0].PRDTL,0);

	///>>>>>>>>get PRDT(int Command Table)<<<<<<<<<<<<
	prdt = cmdtbl->CMD_PRDT;

	///detect MSI capability
	bus = (HBA.PCI_Dev->BDF >> 16) & 0xff;
	device = (HBA.PCI_Dev->BDF >> 11) & 0x1f;
	function = (HBA.PCI_Dev->BDF >> 8) & 0x7;
	index = HBA.PCI_Dev->CapabilitiesPointer;

	while(index != 0)
	{
		value = Read_PCI_Config(bus,device,function,index);
		if((value & 0xff) == 0x05)
			break;

		index = (value >> 8) & 0xff;
	}

	color_printk(GREEN,BLACK,"Capability ID:%#04x,Pointer:%#04x,%#06x,",value & 0xff,((value >> 8) & 0xff),(value >> 16) & 0xffff);
	color_printk(GREEN,BLACK,"%#010x,",Read_PCI_Config(bus,device,function,index + 4));
	color_printk(GREEN,BLACK,"%#010x,",Read_PCI_Config(bus,device,function,index + 8));
	color_printk(GREEN,BLACK,"%#010x\n",Read_PCI_Config(bus,device,function,index + 12));

	///configuration MSI
	value = 0xfee00000;	///MSI address Lower
	Write_PCI_Config(bus,device,function,index + 4,value);
	value = 0;		///MSI address Upper
	Write_PCI_Config(bus,device,function,index + 8,value);
	value = 0x2e;		///MSI data
	Write_PCI_Config(bus,device,function,index + 12,value);

	///MSI control
	///bit 07:64 Bit Address Capable (C64)
	///bit 04-06:Multiple Message Enable (MME)
	///bit 01-03:Multiple Message Capable (MMC)
	///bit 00:MSI Enable (MSIE)
	value = Read_PCI_Config(bus,device,function,index);
	value = value | 0x10000;
	Write_PCI_Config(bus,device,function,index,value);

	///register interrupt
	register_irq(0x2e, NULL , &AHCI_handler, (unsigned long)&AHCI_request, &AHCI_int_controller, "AHCI");

	///bit 28-31:Interface Communication Control (ICC)
	///bit 04:FIS Receive Enable (FRE)
	///bit 01:Spin-Up Device (SUD)
	///bit 00:Start (ST)
	HBA.PxCR[0].PxCMD = HBA.PxCR[0].PxCMD | 0x10000003;

	///Port x Serial ATA Error
	value = HBA.PxCR[0].PxSERR;
	if(value)
		HBA.PxCR[0].PxSERR = value;

	///Port x Interrupt Status
	value = HBA.PxCR[0].PxIS;
	if(value)
		HBA.PxCR[0].PxIS = value;

	io_mfence();

	///Port x Interrupt Enable
	///bit 05:Descriptor Processed Interrupt Enable (DPE)
	///bit 00:Device to Host Register FIS Interrupt Enable (DHRE)
	HBA.PxCR[0].PxIE = 0x20;
}

void AHCI_exit()
{
	unregister_irq(0x2e);
}
